package com.chrray.position.util.tx;


import com.alibaba.fastjson.JSONObject;
import com.qcloud.cos.COSClient;

import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.transfer.*;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.chrray.position.util.tx.OssFileUtil.createCOSClient;


/**
 * @author LM
 * @date 2023/11/22 14:57
 */
@Slf4j
public class OssFileTransferUtil {

    // 创建 TransferManager 实例，这个实例用来后续调用高级接口
    static TransferManager createTransferManager(String secretId,String secretKey,String regionAdd) {
        // 创建一个 COSClient 实例，这是访问 COS 服务的基础实例。
        // 详细代码参见本页: 简单操作 -> 创建 COSClient
        COSClient cosClient = createCOSClient(secretId,secretKey,regionAdd);

        // 自定义线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
        // 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
        ExecutorService threadPool = Executors.newFixedThreadPool(32);

        // 传入一个 threadpool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
        TransferManager transferManager = new TransferManager(cosClient, threadPool);

        // 设置高级接口的配置项
        // 分块上传阈值和分块大小分别为 5MB 和 1MB
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
        transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
        transferManager.setConfiguration(transferManagerConfiguration);

        return transferManager;
    }

    static void shutdownTransferManager(TransferManager transferManager) {
        // 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。
        // 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
        transferManager.shutdownNow(true);
    }


    /**
     * 上传高级接口
     *
     * @param bucketName    存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
     * @param key           对象键(Key)是对象在存储桶中的唯一标识。
     * @param localFilePath 本地文件路径 /path/to/localFile
     * @return
     */
    public static UploadResult uploadFile(String secretId,String secretKey,String regionAdd,String bucketName, String key, String localFilePath) throws Exception {
        // 使用高级接口必须先保证本进程存在一个 TransferManager 实例，如果没有则创建
// 详细代码参见本页：高级接口 -> 创建 TransferManager
        TransferManager transferManager = createTransferManager(secretId,secretKey,regionAdd);
        File localFile = new File(localFilePath);


        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile);


// 设置存储类型（如有需要，不需要请忽略此行代码）, 默认是标准(Standard), 低频(standard_ia)
// 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
//        putObjectRequest.setStorageClass(StorageClass.Standard_IA);


//若需要设置对象的自定义 Headers 可参照下列代码,若不需要可省略下面这几行,对象自定义 Headers 的详细信息可参考 https://cloud.tencent.com/document/product/436/13361
//        ObjectMetadata objectMetadata = new ObjectMetadata();


//若设置 Content-Type、Cache-Control、Content-Disposition、Content-Encoding、Expires 这五个字自定义 Headers，推荐采用 objectMetadata.setHeader()
//        objectMetadata.setHeader(key, value);
//若要设置 “x-cos-meta-[自定义后缀]” 这样的自定义 Header，推荐采用
/*        Map<String, String> userMeta = new HashMap<String, String>();
        userMeta.put("x-cos-meta-[自定义后缀]", "value");
        objectMetadata.setUserMetadata(userMeta);*/


//        putObjectRequest.withMetadata(objectMetadata);


        try {
            // 高级接口会返回一个异步结果Upload
            // 可同步地调用 waitForUploadResult 方法等待上传完成，成功返回 UploadResult, 失败抛出异常
            Upload upload = transferManager.upload(putObjectRequest);
            showTransferProgress(upload);
            UploadResult uploadResult = upload.waitForUploadResult();
            log.info(JSONObject.toJSONString(uploadResult));
            return uploadResult;
        } catch (CosServiceException e) {
            e.printStackTrace();
            throw new Exception();
        } catch (CosClientException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            // 确定本进程不再使用 transferManager 实例之后，关闭即可
            // 详细代码参见本页：高级接口 -> 关闭 TransferManager
            shutdownTransferManager(transferManager);
        }
        return null;
    }

    /**
     * 上传高级接口
     *
     * @param bucketName  存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
     * @param key         对象键(Key)是对象在存储桶中的唯一标识。
     * @param inputStream 上传的 InputStream 类型的流
     * @return
     * @throws Exception
     */
    public static UploadResult uploadInputStream(String secretId,String secretKey,String regionAdd,String bucketName, String key, InputStream inputStream) throws Exception {
        // 使用高级接口必须先保证本进程存在一个 TransferManager 实例，如果没有则创建
// 详细代码参见本页：高级接口 -> 创建 TransferManager
        TransferManager transferManager = createTransferManager(secretId,secretKey,regionAdd);


        ObjectMetadata objectMetadata = new ObjectMetadata();
// 上传的流如果能够获取准确的流长度，则推荐一定填写 content-length
// 如果确实没办法获取到，则下面这行可以省略，但同时高级接口也没办法使用分块上传了
//        objectMetadata.setContentLength(inputStreamLength);

        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, objectMetadata);

// 设置存储类型（如有需要，不需要请忽略此行代码）, 默认是标准(Standard), 低频(standard_ia)
// 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
//        putObjectRequest.setStorageClass(StorageClass.Standard_IA);

        try {
            // 高级接口会返回一个异步结果Upload
            // 可同步地调用 waitForUploadResult 方法等待上传完成，成功返回 UploadResult, 失败抛出异常
            Upload upload = transferManager.upload(putObjectRequest);
            showTransferProgress(upload);
            UploadResult uploadResult = upload.waitForUploadResult();
            log.info(JSONObject.toJSONString(uploadResult));
            return uploadResult;
        } catch (CosServiceException e) {
            e.printStackTrace();
            throw new Exception();
        } catch (CosClientException e) {
            e.printStackTrace();
            throw new Exception();
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new Exception();
        } finally {

// 确定本进程不再使用 transferManager 实例之后，关闭即可
// 详细代码参见本页：高级接口 -> 关闭 TransferManager
            shutdownTransferManager(transferManager);
        }
    }

    static void showTransferProgress(Transfer transfer) {
        // 这里的 Transfer 是异步上传结果 Upload 的父类
        System.out.println(transfer.getDescription());


        // transfer.isDone() 查询上传是否已经完成
        while (transfer.isDone() == false) {
            try {
                // 每 2 秒获取一次进度
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                return;
            }


            TransferProgress progress = transfer.getProgress();
            long sofar = progress.getBytesTransferred();
            long total = progress.getTotalBytesToTransfer();
            double pct = progress.getPercentTransferred();
            log.info("upload progress: [%d / %d] = %.02f%%\n", sofar, total, pct);
        }


        // 完成了 Completed，或者失败了 Failed
        System.out.println(transfer.getState());
    }

}
